package com.summer.data.dialect;

import com.summer.data.Jsql;
import com.summer.data.exception.SqlException;
import com.summer.data.jql.Context;
import com.summer.data.jql.behavior.FilterAble;
import com.summer.data.jql.compose.AndPack;
import com.summer.data.jql.concept.func.Nvl;
import com.summer.data.logging.Logger;
import com.summer.data.logging.LoggerFactory;
import com.summer.data.parameter.ParameterStruct;
import com.summer.data.rs.Extractor;
import com.summer.data.rs.datatype.PageInfo;
import com.summer.data.rs.extractor.KeysExtractor;
import com.summer.data.rs.extractor.OneRecordExtractor;
import com.summer.data.rs.map.AbstractResultSetMapFun;
import com.summer.data.table.BaseColumn;
import com.summer.data.table.BaseTable;

import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @author 345480567@qq.com
 *
 * SEE : https://www.cnblogs.com/-xlp/p/8617760.html
 */

public interface Dialect {

    Logger logger = LoggerFactory.getLogger(Dialect.class);

    /********* DDL ********/
    <T extends BaseTable> List<Jsql> buildCreateTableSql(Class<T> clazz);
    <T extends BaseTable> List<Jsql> buildModifyTableSql(Class<T> clazz);

    <T extends BaseTable> Jsql buildDropTableSql(Class<T> clazz);

    /********* Table DML ********/
    Jsql buildInsertSql(Object obj);

    <T extends BaseTable> Jsql buildUpdateSql(T t, FilterAble... exps);

//    boolean isSupportUpsert();
//
//    <T extends BaseTable> Jsql buildUpsertSql(T t, FilterAble... exps);

    Jsql buildSelectSql(Class<? extends BaseTable> clazz, FilterAble... exps);

    <T extends BaseTable> Jsql buildDeleteSql(T t, FilterAble... exps);

    Jsql buildDeleteSql(Class<? extends BaseTable> clazz, FilterAble... exps);

//    static Jsql buildWhereSql(Dialect dialect, FilterAble... exps) {
//        if (exps.length > 0) {
//            AndPack andPack = new AndPack(exps);
//            Jsql jsql = andPack.toFilterJsql(new Context(false), dialect);
//            Jsql whereJsql = new Jsql(" WHERE ");
//            whereJsql.append(jsql);
//            return whereJsql;
//        } else {
//            return new Jsql("");
//        }
//    }

    /********* Jsql DML ********/
    String getCountSql(String string);

    Jsql getCountSql(Jsql jsql);

//    default String getPageSql(String string, Integer offset, Integer pageSize) {
//        String pageSql = String.format("%s limit %d, %d ", string, offset, pageSize);
//        return pageSql;
//    }

    String getPageSql(String string, Integer offset, Integer pageSize);

    String getPageSql(String string, PageInfo pageInfo);

    Jsql getPageSql(Jsql jsql, PageInfo pageInfo);

    Jsql getNvlJsql(Context ctx, Nvl nvlFunc);

    Map<Class<?>, JDBCType> getJDBCTypeMap();

    JDBCType getJDBCType(Class<?> clazz);

    JDBCType getJDBCType(BaseColumn<?> clazz);

    void setBoolean(PreparedStatement ps, int i, Object value);

    void setShort(PreparedStatement ps, int i, Object value);

    void setInt(PreparedStatement ps, int i, Object value);

    void setLong(PreparedStatement ps, int i, Object value);

    void setFloat(PreparedStatement ps, int i, Object value);

    void setDouble(PreparedStatement ps, int i, Object value);

    void setBigDecimal(PreparedStatement ps, int i, Object value);

    void setDate(PreparedStatement ps, int i, Object value);

    void setTime(PreparedStatement ps, int i, Object value);

    void setTimestamp(PreparedStatement ps, int i, Object value);

    void setString(PreparedStatement ps, int i, Object value);

    /**
     * 默认采用 mysql 为底版
     *
     * @param ps    PreparedStatement
     * @param i     列序号
     * @param value 设定值
     */
    void setClob(PreparedStatement ps, int i, Object value);

    void setBlob(PreparedStatement ps, int i, Object value);

    void prepare(PreparedStatement ps, int i, ParameterStruct parameterStruct);

    JDBCType getParameterJDBCType(ParameterStruct parameterStruct);

    void readValue(ResultSet rs, int i);

    String getLikeSql();
    String getLeftLikeSql();
    String getRightLikeSql();
    String getFixedLikeSql(int leftFix, int rightFix);

    default Extractor<Integer> getCountExtracter() {
        ResultSetToCountFun mapFun = new ResultSetToCountFun();
        return new OneRecordExtractor<>(mapFun, true);
    }

    default Boolean isResultUseColumnName() {
        return false;
    }

    class ResultSetToCountFun extends AbstractResultSetMapFun<Integer> {

        @Override
        public Integer map(ResultSet resultSet, Dialect dialect) {
            try {
                return resultSet.getInt(1);
            } catch (SQLException e) {
                throw new SqlException(e);
            }
        }
    }

    KeysExtractor buildKeysExtractor(BaseTable table);

    static Jsql buildWhereSql(Dialect dialect, FilterAble... exps) {
        if (exps.length > 0) {
            AndPack andPack = new AndPack(exps);
            Jsql jsql = andPack.toFilterJsql(new Context(false), dialect);
            Jsql whereJsql = new Jsql(" WHERE ");
            whereJsql.append(jsql);
            return whereJsql;
        } else {
            return new Jsql("");
        }
    }

    static Jsql buildOnSql(Dialect dialect, FilterAble... exps) {
        if (exps.length > 0) {
            AndPack andPack = new AndPack(exps);
            Jsql jsql = andPack.toFilterJsql(new Context(false), dialect);
            Jsql whereJsql = new Jsql(" ON ");
            whereJsql.append(jsql);
            return whereJsql;
        } else {
            return new Jsql("");
        }
    }

}
